home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1833 / 1833.xpi / modules / yoonoService.js < prev    next >
Text File  |  2009-12-16  |  30KB  |  848 lines

  1. var EXPORTED_SYMBOLS = ["YOONO_CMPT"];
  2.  
  3. Components.utils.import("resource://yoono/yoonoKeyValueDB.js");
  4. Components.utils.import("resource://yoono/yoonoLog.js");
  5. Components.utils.import("resource://yoono/yoonoPrefs.js");
  6. Components.utils.import("resource://yoono/yoonoServerConnection.js");
  7. Components.utils.import("resource://yoono/yoonoBkmSync.js");
  8. Components.utils.import("resource://yoono/yoonoStorage.js");
  9.  
  10. try {
  11.  
  12. // Globals
  13. const CI = Components.interfaces;
  14. const CL = Components.classes;
  15. const CONSOLESERVICE = CL["@mozilla.org/consoleservice;1"].getService(CI.nsIConsoleService);
  16. const DIRSERVICE = CL['@mozilla.org/file/directory_service;1'].getService(CI.nsIProperties);
  17.  
  18. const YOONO_ID = "{d9284e50-81fc-11da-a72b-0800200c9a66}";
  19.  
  20. // yoono stuff
  21. var yoono={};
  22. var log = {info:function(m) {CONSOLESERVICE.logStringMessage(m);},
  23.     debug:function(m) {CONSOLESERVICE.logStringMessage(m);},
  24.     warn:function(m){CONSOLESERVICE.logStringMessage(m);},
  25.     error:function(m){CONSOLESERVICE.logStringMessage(m);},
  26.     fatal:function(m){CONSOLESERVICE.logStringMessage(m);}};
  27.  
  28. // List of preferences that must absolutely never be overriden by the server
  29. // In case you need to add such a preference, just set it to true in the object below:
  30. var safePreferences = {
  31.   bookmarksLastModified: true,
  32.   synchroaction: true,
  33.   synchroask : true,
  34.   syncid : true,
  35.   userid : true
  36. }
  37.  
  38. // interfaces
  39. const PREFSSERVICE = CL["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefService);
  40. const WMED = CL['@mozilla.org/appshell/window-mediator;1'].getService(CI.nsIWindowMediator);
  41.  
  42. const PREFS = PREFSSERVICE.getBranch("extensions.yoono.");
  43.  
  44.  
  45.  
  46.  
  47. // chromeUI : le composant en lui meme
  48. function chromeUI() {
  49.   this._started = false;
  50.   this.uninstallDone = false;
  51.   
  52.   this.timerLogIn = CL['@mozilla.org/timer;1'].createInstance(CI.nsITimer);
  53.   // List of incompatible extensions. Ids MUST be lower case
  54.   this.dualSidebarIncompatibleExtensions = {
  55.     '{097d3191-e6fa-4728-9826-b533d755359d}' : 'All-in-One Sidebar',
  56.     '{0eaf175c-0c46-4932-ab7d-f45d6c46f367}' : 'Ez Sidebar'
  57.   };
  58.  
  59.   // List of incompatible bkm sync extensions. Ids MUST be lower case
  60.   this.bkmSyncIncompatibleExtensions = {
  61.     '{18b9b035-67a2-4db6-bf42-3993ae4d89a7}' : 'Bookmarks on line',
  62.     '{66580a2f-7538-498d-b049-a540e77aad9d}' : 'AbstractMouse.com Shared Bookmarks',
  63.     '{e133f188-27e7-401d-be2e-804643793acb}' : 'Bookmarks Synchronizer',
  64.     'foxmarks@kei.com' : 'Foxmarks Bookmark Synchronizer',
  65.     '{32537848-7d38-4ee2-b5a2-47562e69c59e}' : 'Foxylicious',
  66.     'browserstate@google.com' : 'Google Browser Sync'
  67.   };
  68.  
  69.   this.statDelay = 24 * 3600 ; // stats sent every 24 hours
  70.  
  71.   this.uninstallWhenQuitting = false;
  72.  
  73.   
  74.   // initialisation of the component. (called by yoonoOverlay.js)
  75.   this.init = function (y) {
  76.     
  77.     // ne s'execute qu'une seule fois par session
  78.     if (this._started)
  79.       return;
  80.     this._started = true;
  81.     
  82.       yoono = y;
  83.       log = y.log;
  84.  
  85.     // wait 5 seconds for sidebar to be ready before checking for stats
  86.     this.timerLogIn.initWithCallback(this, 5000 , this.timerLogIn.TYPE_ONE_SHOT);
  87.     
  88.     this.startGlobalServices(yoono.yextif);
  89.       
  90.       yoono.bkm.start(yoono);
  91.     
  92.   };
  93.   
  94.   /*******************/
  95.   /* Global services */
  96.   this._globalService = null;
  97.   this.getYServices = function () {
  98.     return this._globalService;
  99.   }
  100.   this.startGlobalServices = function (yextif) {
  101.     if (this._globalService)  return;
  102.     
  103.     try {
  104.       log.debug("loading global services with yextif = "+yextif);
  105.       
  106.       // Get *the* hidden window
  107.       var hiddenWindow = Components.classes["@mozilla.org/appshell/appShellService;1"]
  108.           .getService(Components.interfaces.nsIAppShellService)
  109.           .hiddenDOMWindow;
  110.       var doc=hiddenWindow.document;
  111.  
  112.       // Create our hidden iframe
  113.       var iframe=doc.createElement("iframe");
  114.       iframe.setAttribute("src","chrome://yoonosb/content/js/services/hiddenIFrame.html");
  115.       doc.lastChild.appendChild(iframe);
  116.       
  117.       
  118.       // Wait for load in order to retrieve global object for YServices
  119.       var _self=this;
  120.       iframe.addEventListener("load",function () {
  121.         try {
  122.           _self._globalService = iframe.contentWindow.loadServices(yextif);
  123.         } catch(e) {
  124.           log.exception(e);
  125.         }
  126.       }, true);
  127.     
  128.     } catch(e) {
  129.       log.exception(e);
  130.     }
  131.   }
  132.   /* End global services */
  133.   
  134.   
  135.     //Timer to handle timeout on requests
  136.     var requestTimeoutTimer = function(request, handler) {
  137.         this._self = this;
  138.         this.request = request;
  139.         this.handler = handler;
  140.         this.timer = CL['@mozilla.org/timer;1'].createInstance(CI.nsITimer);
  141.         var delay = YOONO_PREFS.get('request.timeout') * 1000; // la pref est en secondes
  142.         this.timer.initWithCallback(this, delay, this.timer.TYPE_ONE_SHOT);
  143.     }
  144.  
  145.     requestTimeoutTimer.prototype = {
  146.         notify : function(timer) {
  147.             try {
  148.                 log.debug("Request " + this.request.tstamp + " timed out" );
  149.                 this.request.abort();
  150.                 // yoono.dialogs.setThrobberFailed();
  151.                 if(this.handler)
  152.                   this.handler(this.request, '');
  153.                 // cleanup
  154.                 try     { this._self.destroy() } catch(e) {log.exception(e);}
  155.                 finally { this._self = null }
  156.             } catch(e) {
  157.                 log.exception(e);
  158.             }
  159.         }
  160.     }
  161.     // Wrapper method to be used when no headers are to be added to request
  162.     // (historic...)
  163.     // (don't use $this because this function is called without his environnement)
  164.     this.sendRequest = function(url, method, mode, body, handler) {
  165.         method = ('GET' == method)?'GET':'POST';
  166.         return (yoono.main.sendFullRequest(url, method, null, mode, body, handler));
  167.     }
  168.  
  169.     // NOV 2006 : this is the sending method that must now be used almost throughout the application.
  170.     // @author : Xavier Grosjean
  171.     // @param url : destination url
  172.     // @param method : GET or POST
  173.     // @param headers : Object with headers names and values : headers['name'] = value
  174.     // @param mode : sync or async
  175.     // @param body : post payload. If xml, will be serialized and script= will be inserted at begining
  176.     // @param handler : handle for response (or null). Will receive request and xml result as param
  177.     // @returns array with request and result (xml response payload if not async)
  178.     this.sendFullRequest = function(url, method, headers, mode, body, handler) {
  179.         var req = null;
  180.         var result = '';
  181.         try {
  182.             req = CL["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(CI.nsIXMLHttpRequest);
  183.             req.tstamp = 'id #' + new Date().getTime();
  184.             log.info('Sending ' + mode + ' ' + method + ' on ' + url + "\n" + body + "\n>>>>>>>>>>>> end of request " + req.tstamp + " >>>>>>>>>>>>");
  185.             var b=body;
  186.             if('xml' == typeof(body)) body = 'script=' + encodeURIComponent(body.toString());
  187.             
  188.             method = method.toUpperCase();
  189.             var async = ('async' == mode)?true:false;
  190.  
  191.             req.open(method,url,async);
  192.             req.setRequestHeader("Cache-Control", "no-cache");
  193.  
  194.             if('POST' == method) {
  195.                 req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  196.             }
  197.             // If there are headers, add them ...
  198.             if(headers) {
  199.                 for(var name in headers) {
  200.                     req.setRequestHeader(name, encodeURIComponent(headers[name]));
  201.                 }
  202.             }
  203.  
  204.             // Initialize a timer to timeout the request if necessary
  205.             var timerRequest = new requestTimeoutTimer(req, handler);
  206.             req.onload = function (event) {
  207.                 var result = '';
  208.                 timerRequest.timer.cancel();
  209.                 try {
  210.                     var request = event.target;
  211.                     var suggestUrl  = '';
  212.                     if((4 == request.readyState) && (200 == request.status)) {
  213.                         try {
  214.                             result = new XML(request.responseText.replace(/<\?xml.*?\?>\n?/g, ""));
  215.                         } catch(e) {
  216.                             log.error("Error parsing Response " + e);
  217.                             log.error("Response Text: " + request.status + "\n" + request.responseText + "\n<<<<<<<<<<< end of response " + request.tstamp + "<<<<<<<<<<<");
  218.                             yoono.dialogs.setThrobberFailed();
  219.                             return;
  220.                         }
  221.                         log.info("Response " + request.status + "\n" + result + "\n<<<<<<<<<<< end of response <<<<<<<<<<< " + request.tstamp);
  222.                         yoono.main.getServerContextData(result);
  223.                     } else {
  224.                         log.error("Response " + request.status );
  225.                         yoono.dialogs.setThrobberFailed();
  226.                     }
  227.                     if(handler)
  228.                     handler(request, result, body);
  229.                 } catch(e) {
  230.                     log.exception(e);
  231.                     yoono.dialogs.setThrobberFailed();
  232.                 }
  233.             };
  234.             req.onerror = function (e) {
  235.                 try {
  236.                     timerRequest.timer.cancel();
  237.                     log.error("Request error on "+b );
  238.                     yoono.dialogs.setThrobberFailed();
  239.                     if(handler)
  240.                         handler(req, 'error', body);
  241.                 } catch(e) {
  242.                     log.exception(e);
  243.                     yoono.dialogs.setThrobberFailed();
  244.                 }
  245.             };
  246.             req.send(body);
  247.             if(!async) {
  248.                 if((4 == req.readyState) && (200 == req.status)) {
  249.                     result = new XML(req.responseText.replace(/<\?xml.*?\?>\n?/g, ""));
  250.                     yoono.main.getServerContextData(result);
  251.                 }
  252.             }
  253.         } catch(e) {
  254.             log.exception(e);
  255.             yoono.dialogs.setThrobberFailed();
  256.         }
  257.         return (new Array(req, result));
  258.     }
  259.  
  260.  
  261.  
  262.  
  263.   this.notify = function(timer){
  264.     try {
  265.         this.checkStats();
  266.     } catch(e) {
  267.         log.exception(e);
  268.     }
  269.   };
  270.  
  271.  
  272. //check if stats must be sent
  273.  
  274.   this.checkStats = function(mode) {
  275.     var win = WMED.getMostRecentWindow("navigator:browser");
  276.     if (!win)
  277.       win = Components.classes["@mozilla.org/appshell/appShellService;1"]
  278.           .getService(Components.interfaces.nsIAppShellService)
  279.           .hiddenDOMWindow;
  280.     
  281.     var offset = 0;
  282.     try {
  283.  
  284.       // check if stats must be sent
  285.       // date is read in prefs cache so that not cheatable from about:config without relaunching FF
  286.       var dateNow = Math.round((new Date().getTime()) / 1000);
  287.       var dateStat = YOONO_PREFS.get('stats.last');
  288.       log.debug('YOONO_CMPT.checkStats checking stats, mode=' + mode + ', now=' + dateNow + ', previous=' + dateStat );
  289.       if((dateStat != 0) || mode) { // first call
  290.         if(mode || (dateNow >= (dateStat + this.statDelay))) {
  291.           var script = <server-script version="1.0"/>;
  292.           var context = <context>
  293.                           <locale>{yoono.utils.getLocale()}</locale>
  294.                           <version>{yoono.yextif.methods.getVersion()}</version>
  295.                           <agent>{yoono.utils.getUserAgent(win)}</agent>
  296.                           <appversion>{yoono.utils.getUserAgentAppVersion(win)}</appversion>
  297.                           <oscpu>{yoono.utils.getUserAgentOsCpu(win)}</oscpu>
  298.                           <platform>{yoono.utils.getUserAgentPlatform(win)}</platform>
  299.                           <client>{yoono.yextif.methods.getClientType()}</client>
  300.                           <user-id>{YOONO_PREFS.get('userid')}</user-id>
  301.                         </context>;
  302.           script.appendChild(context);
  303.           var stat = null;
  304.           if(mode) {
  305.             stat = <stats begins={dateStat} ends={dateNow} command={mode}/>;
  306.           } else {
  307.             stat = <stats begins={dateStat} ends={dateNow}/>;
  308.           }
  309.           var statData = PREFSSERVICE.getBranch("extensions.yoono.stats.data.");
  310.           var childArray = statData.getChildList('', {});
  311.           var prefName = '';
  312.           var type = '';
  313.           var tag = null;
  314.           var value = 0;
  315.           var tagData = null;
  316.  
  317.           for (var i = childArray.length ; i-- > 0 ; ) {
  318.             prefName = childArray[i];
  319.             type = statData.getPrefType(prefName);
  320.             // get bits from string using '.' as separator
  321.             tagData = prefName.split('.');
  322.             // re-join extra bits of string beyond the 3rd one.
  323.             if(tagData[3]) tagData[2] = tagData.splice(2).join('.');
  324.             prefName = 'stats.data.' + prefName;
  325.             value = YOONO_PREFS.get(prefName);
  326.             if(!value) continue;
  327.             tag = <{tagData[0]} />;
  328.             if(tagData[1]) tag.@type = tagData[1];
  329.             if(tagData[2]) tag.@ui = tagData[2];
  330.             tag.@value = value;
  331.             stat.appendChild(tag);
  332.             YOONO_PREFS.set(prefName, 0, type);
  333.           }
  334.           // Send the date each yoodget was displayed first 
  335.           try {
  336.             var firstDisplayDates = YOONO_KEYVALUEDB.getKeyValueList('sidebarFirstDisplayDate');
  337.             var widgetName = '';
  338.             for(var aDisplayDateKey in firstDisplayDates) {
  339.               widgetName = aDisplayDateKey.replace(/sidebarFirstDisplayDate/, '');
  340.               stat.appendChild(<widget-first-displayed ui={widgetName} value={firstDisplayDates[aDisplayDateKey]}/>);
  341.             }
  342.           } catch(e){
  343.             log.exception(e);
  344.           }
  345.  
  346.           // if code below fails, still send other stats
  347.           try {
  348.             stat.appendChild(<nosynchro value={YOONO_PREFS.get('nosynchro')}/>);
  349.           } catch(e){}
  350.           try {
  351.             var marketingCampaign = YOONO_KEYVALUEDB.getKeyValue('sidebarMarketingCampaign') || '0' ;
  352.             if('0' != marketingCampaign) {
  353.               stat.appendChild(<marketing type="campaign" value={marketingCampaign}/>);
  354.             }
  355.           } catch(e){}
  356.           try {
  357.             var yoodgetList = YOONO_KEYVALUEDB.getKeyValue('sidebarYoodgetList') || '' ;
  358.             // FF2 does not support JSON
  359.             // FF3.0 and FF3.1 support it a very different way, so fuck JSON
  360.             var tab = yoodgetList.match(/"yuid"/g);
  361.             var nb = 0;
  362.             if(tab) nb = tab.length;
  363.             stat.appendChild(<widget type="installed" value={nb}/>);
  364.           } catch(e){}
  365.           try {
  366.             var installDate = PREFS.getIntPref("install_date");
  367.             var age = dateNow - installDate;
  368.             stat.appendChild(<install_age value={age}/>);
  369.           } catch(e){}
  370.           var update = YOONO_PREFS.get('updated.from') || '';
  371.           if(update) {
  372.             stat.appendChild(<update old={update} new={YOONO_PREFS.get('release')} client={yoono.yextif.methods.getClientType()}/>);
  373.             YOONO_PREFS.set('updated.from', '', PREFS.PREF_STRING);
  374.           }
  375.           
  376.           try {
  377.             var yoolinkEnabled = PREFS.getBoolPref("highlight.enable");
  378.             if (yoolinkEnabled)
  379.               stat.appendChild(<yoolink type="underline-enable" value="1" />);
  380.             else
  381.               stat.appendChild(<yoolink type="underline-disable" value="1" />);
  382.           } catch(e){}
  383.           
  384.           script.appendChild(stat);
  385.           YOONO_PREFS.set('stats.last', dateNow, PREFS.PREF_INT);
  386.           // Check if stat must be sent.
  387.           // Check is done this late because if we don't send them we still want them to be reset
  388.           var noStat = YOONO_PREFS.get('stats.no') ;
  389.           if(noStat && ('manual' != mode)) {
  390.             log.debug('YOONO_CMPT.checkStats stats sending disabled' );
  391.           } else {
  392.             // Send a request to increment the counter of sent statistics
  393.             yoono.main.sendRequest(YOONO_PREFS.get('serverurl') + 'rest/counters/one/client-stats/send', 'POST', 'async', null, null);
  394.             // Send the statistics
  395.             var _self = this;
  396.             yoono.main.sendRequest(YOONO_PREFS.get('serverurl') + 'linkserver', 'POST', 'async',
  397.                                    script, function(request, result, body){_self.statResult(request, result, body)});
  398.           }
  399.         } else {
  400.           offset = dateNow - dateStat;
  401.         }
  402.       } else {
  403.         YOONO_PREFS.set('stats.last', dateNow, PREFS.PREF_INT);
  404.       }
  405.     } catch(e) {
  406.       log.exception(e);
  407.     }
  408.     // send stats every 24 hours
  409.     var delay = (this.statDelay - offset ) * 1000 ;
  410.     this.timerLogIn.initWithCallback(this, delay , this.timerLogIn.TYPE_ONE_SHOT);
  411.   };
  412.  
  413.   this.statResult = function(request, result, body) {
  414.     var xml = request.responseXML;
  415.     if(!xml) {
  416.       yoono.main.sendRequest(YOONO_PREFS.get('serverurl') + 'rest/counters/one/client-stats-error/timeout', 'POST', 'async', null, null);
  417.       return;
  418.     }
  419.     var dom = xml.documentElement;
  420.     var msg = dom.getElementsByTagName('display-message');
  421.     if (msg && msg[0]) {
  422.       var code = msg[0].getAttribute('code');
  423.       // Send a request to increment the counter of sent statistics
  424.       yoono.main.sendRequest(YOONO_PREFS.get('serverurl') + 'rest/counters/one/client-stats-error/' + encodeURIComponent(code), 'POST', 'async', null, null);
  425.     }
  426.   };
  427.  
  428.  
  429.   // increments a stat counter in prefs
  430.   this.addStat = function(params, step) {
  431.     if(null == step) step = 1;
  432.     var namePref = 'stats.data.' + params.join('.');
  433.     var current = parseInt(YOONO_PREFS.get(namePref) || 0);
  434.     current += parseInt(step);
  435.     if(PREFS.PREF_INT != PREFS.getPrefType(namePref)) {
  436.       try {
  437.         // pref may not exist
  438.         PREFS.clearUserPref(namePref);
  439.       } catch(e) {}
  440.     }
  441.  
  442.     YOONO_PREFS.set(namePref, current, PREFS.PREF_INT);
  443.   }
  444.  
  445.   this.setStat = function(params, value) {
  446.     var namePref = 'stats.data.' + params.join('.');
  447.     var type = PREFS.PREF_INT;
  448.     if((value + 1) != (1 + value))
  449.       type = PREFS.PREF_STRING;
  450.     YOONO_PREFS.set(namePref, value, type);
  451.   }
  452.  
  453.   this.inprogress = function () {
  454.     return yoono.dialogs._connecting;
  455.   };
  456.  
  457.  
  458.   // check if incompatible extension 
  459.   this.checkIncompatibility = function(extensionArray) {
  460.       var extmgr = CL['@mozilla.org/extensions/manager;1'].getService(CI.nsIExtensionManager);
  461.       var result = false;
  462.       var extensionList = extmgr.getItemList(Components.interfaces.nsIUpdateItem.TYPE_ANY, {});
  463.       var extId = '';
  464.       for(var ind=0 ; ind < extensionList.length; ind ++) {
  465.           extId = extensionList[ind].id;
  466.           extId = extId.toLowerCase();
  467.           // TODO : check if disabled...not that easy...
  468.           if(extId in extensionArray) {
  469.             result = true;
  470.             log.debug('Incompatible extension detected: ' + extId);
  471.           }
  472.       }
  473.       return result;
  474.   };
  475.  
  476.   this.getUserInfo = function(login, mode, handler) {
  477.     var async = ('async' == mode)?true:false;
  478.     var script = <server-script version="1.0"/> ;
  479.     script.appendChild( <context> <user-id>{login}</user-id> </context>);
  480.     script.appendChild( <get-user-data/>);
  481.     var url = YOONO_PREFS.get('serverurl') + 'linkserver';
  482.     result = yoono.main.sendRequest(url, 'POST', async, script, handler) ;
  483.     return (result);
  484.   };
  485.   // user may change his password anytime
  486.   this.getUserCredential = function() {
  487.     var userId = PREFS.getCharPref("userid");
  488.     if(!userId) return null; 
  489.     var result = new Object;
  490.     // anonymous users have no ':' in their userid
  491.     if(!userId.match(':')) {
  492.       result.login = null;
  493.       result.password = null;
  494.       result.userId = userId;
  495.       result.anonymous = true;
  496.     } else {
  497.       var userData = userId.split(':');
  498.       result.userId = userId;
  499.       result.login = userData[0];
  500.       result.password = userData[1];
  501.       result.anonymous = false;
  502.     }
  503.     return(result);
  504.   };
  505.  
  506.   this.escapeFileName = function(fileName) {
  507.     var escFileName = fileName.replace(/[\/\\" :]/g, '_');
  508.     return(escFileName);
  509.   };
  510.  
  511.   this.registerUser = function (login, passwd) {
  512.     log.debug("Registering new user : login="+login);
  513.     YOONO_PREFS.set('userid', login + ':' + passwd, PREFS.PREF_STRING);
  514.     // must refresh user authentication data for memo handling
  515.   };
  516.  
  517.   // Called from the sidebar to log an existing user installing the extension
  518.   this.changeUser = function(aNewuserId, aSyncMode) {
  519.     var currentCredentials = this.getUserCredential();
  520.     // When creating the account, currentCredentials are empty
  521.     if(!currentCredentials) {
  522.       currentCredentials = {
  523.         'userId': '',
  524.         'login' : ''
  525.       }
  526.     }
  527.     // If no change, nothing to do
  528.     if(currentCredentials.userId == aNewuserId) return;
  529.  
  530.     var newCredentials = aNewuserId.split(':');
  531.  
  532.     if( aSyncMode == null ) {
  533.       aSyncMode = 'manual-sync';
  534.     }
  535.     if (YOONO_PREFS.get('nosynchro')) {
  536.       aSyncMode = 'no-sync';
  537.     }
  538.     log.backtrace("Changing current user : aSyncMode="+aSyncMode);
  539.     
  540.     YOONO_PREFS.set('userid', aNewuserId, PREFS.PREF_STRING);
  541.     
  542.     // Switch database files only if login has changed (not password)
  543.     if(newCredentials[0] != currentCredentials.login) {
  544.       YOONO_STORAGE.switchDBStorage();
  545.       YOONO_KEYVALUEDB.closeDB();    // New db will open itself the first time it will be needed (wow !)
  546.     }
  547.  
  548.     yoono.server.launch('connect',aSyncMode);
  549.   };
  550.  
  551.  
  552.  
  553.   // Processing of server-sent preferences
  554.   this.getServerContextData = function(xml) {
  555.     try {
  556.       var contextPrefs = xml.context['client-prefs'];
  557.       var pref = '';
  558.       // toString is absolutely mandatory in comparaison operators
  559.       for each (var indP in contextPrefs.pref) {
  560.         pref = indP.@name.toString();
  561.         // Test if this pref is allowed to be overriden by a server pref
  562.         if(! (pref in safePreferences)) {
  563.           switch(indP.@type.toString()) {
  564.             case 'int':
  565.               YOONO_PREFS.set(pref, indP.@value, PREFS.PREF_INT);
  566.               break;
  567.             case 'string':
  568.               YOONO_PREFS.set(pref, indP.@value, PREFS.PREF_STRING);
  569.               break;
  570.             case 'bool':
  571.               YOONO_PREFS.set(pref, indP.@value, PREFS.PREF_BOOL);
  572.               break;
  573.           }
  574.         }
  575.       }
  576.       // Don't know why, but need to re-import module
  577.       Components.utils.import("resource://yoono/yoonoKeyValueDB.js");
  578.       var geoLocData = xml.context['geoloc'];
  579.       if(geoLocData) {
  580.         var ip = geoLocData.ip.toString();
  581.         var countryCode = geoLocData.country.@code.toString();
  582.         // Store data for sidebar access
  583.         if(ip) YOONO_KEYVALUEDB.setKeyValue('sidebarIpAddress', ip);
  584.         if(countryCode) YOONO_KEYVALUEDB.setKeyValue('sidebarCountryCode', countryCode);
  585.       }
  586.       var adsData = xml.context['ads'];
  587.       if(adsData) {
  588.         var mivaAuthToken = adsData.miva.toString();
  589.         // Store data for sidebar access
  590.         if(mivaAuthToken) YOONO_KEYVALUEDB.setKeyValue('sidebarMivaAuthToken', mivaAuthToken);
  591.       }
  592.     } catch(e) {
  593.       log.exception(e);
  594.     }
  595.   }
  596.  
  597.   this.uninstallAddonWhenQuitting = function (yoonoItem) {
  598.     try {
  599.       var win = WMED.getMostRecentWindow("Extension:Manager");
  600.       this.goodbyeWizardReturnObject = null;
  601.       if(!this.uninstallWhenQuitting )  {
  602.         this.uninstallWhenQuitting = true;
  603.         // Ask user if he wants to remove his account and data on the server...
  604.         this.goodbyeWizardReturnObject = new Object;
  605.         // the returnObject will contain the script to send to the server, if any, when uninstalling
  606.         var userCredentials = this.getUserCredential();
  607.         if(userCredentials) {
  608.           log.debug("Goodbye popup");
  609.           yoono.dialogs.setDialog('goodbye', this.goodbyeWizardReturnObject);
  610.           log.debug("Returned from uninstall popup: " + this.goodbyeWizardReturnObject.value);
  611.           // If dialog canceled, cancel uninstallation
  612.           if(!this.goodbyeWizardReturnObject.value) {
  613.             log.debug("Cancelling uninstallation");
  614.             if(win) {
  615.               win.gExtensionsViewController.commands.cmd_cancelUninstall(yoonoItem);
  616.               win.focus();
  617.             } else {
  618.               var gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager);
  619.               gExtensionManager.cancelUninstallItem(YOONO_ID);
  620.             }
  621.             this.uninstallWhenQuitting = false;
  622.             // set addons window to front
  623.             return;
  624.           }
  625.         }
  626.       } else {
  627.         this.uninstallWhenQuitting = true;
  628.       }
  629.       if(this.uninstallWhenQuitting) {
  630.         log.debug("uninstallwhenquitting ...");
  631.         this.checkStats('uninstall'); // Force sending stats
  632.         // Close sidebar on each window so that extension does not try to make anymore access to preferences
  633.         // that no longer exist...
  634.         var _self = this;
  635.         yoono.dialogs.traverseBrowsers(function(traversedWindow) {_self.hideYoonoStuff(traversedWindow)});
  636.         if(win) {
  637.           win.focus();
  638.         }
  639.       }
  640.     } catch(e) {
  641.       log.exception(e);
  642.     }
  643.   }
  644.  
  645.   // Fonction appel├⌐e lorsque l'extension doit etre d├⌐sinstall├⌐e, au moment o├╣ on quitte firefox
  646.   this.uninstall = function () {
  647.     try {
  648.       log.debug("uninstallation");
  649.       if(this.goodbyeWizardReturnObject && this.goodbyeWizardReturnObject.script) {
  650.         var script = this.goodbyeWizardReturnObject.script;
  651.         var reqResult = yoono.main.sendRequest(YOONO_PREFS.get('serverurl') + 'linkserver', 'POST', 'sync', script, null);
  652.         try {
  653.           var response = reqResult[1];
  654.           var message = response['display-error'].@code;
  655.           if('ERROR_INVALID_USER_ID' == message) {
  656.           }
  657.           message = response['display-message'].@code.toString();
  658.           // TO BE DONE : what is best in case account could not be deleted ?
  659.           switch(message) {
  660.             case 'MSG_USER_ACCOUNT_REMOVED':
  661.                // SUCCESSDELETED.hidden = false;
  662.             break;
  663.             case 'MSG_USER_ACCOUNT_NOT_REMOVED':
  664.                // SUCCESSKEPT.hidden = false;
  665.             break;
  666.             default:
  667.           }
  668.         } catch(e) {
  669.           log.exception(e);
  670.         }
  671.       }
  672.     } catch(e) {
  673.             log.error("YOONO_CMPT.uninstall : " + e);
  674.     }
  675.  
  676.     YOONO_SERVER.uninstall();
  677.     
  678.     YOONO_BKM.uninstall();
  679.     
  680.     YOONO_KEYVALUEDB.closeDB();
  681.  
  682.     YOONO_STORAGE.closeStorage();
  683.     
  684.     this.cleanupYoonoDir();
  685.  
  686.     log.warn("Remove all yoono preferences");
  687.     PREFS.deleteBranch("");
  688.  
  689.     this.uninstallDone = true;
  690.   }
  691.  
  692.   this.cleanupYoonoDir = function () {
  693.     try {
  694.       var dir = this.getYoonoDir();
  695.       this.removeDirRecursive(dir);
  696.     } catch(e) {
  697.       log.error("Could not remove yoono dir: " + e);
  698.     }
  699.   }
  700.  
  701.   this.getYoonoDir = function  () {
  702.     var rootDir = DIRSERVICE.get("ProfDS", CI.nsIFile) ;
  703.     rootDir.append('yoono');
  704.     return(rootDir);
  705.   }
  706.  
  707.   // Recursive removal of directory. Stolen with (little) shame from nsExtensionManager.js
  708. // the file.remove method is recursive but will fail if file permissions are restrictive...
  709.   this.removeDirRecursive = function (aDir) {
  710.     try {
  711.       aDir.remove(true);
  712.       return;
  713.     } catch (e) {
  714.     }
  715.     var dirEntries = aDir.directoryEntries;
  716.     while (dirEntries.hasMoreElements()) {
  717.       var entry = dirEntries.getNext().QueryInterface(CI.nsIFile);
  718.  
  719.       if (entry.isDirectory()) {
  720.         removeDirRecursive(entry);
  721.       } else {
  722.         entry.permissions = PERMS_FILE;
  723.         entry.remove(false);
  724.       }
  725.     }
  726.     aDir.permissions = PERMS_DIRECTORY;
  727.     aDir.remove(true);
  728.   }
  729.  
  730.   this.loginMyYoonoPages = function(target, event, feedId, vsId) {
  731.     var userData = this.getUserCredential();
  732.     if(!userData || userData.anonymous) return;
  733.     // encoding, may contain utf8
  734.     var login = encodeURIComponent(userData.login);
  735.     var passwd = encodeURIComponent(userData.password);
  736.  
  737.     var url = 'http://api.yoono.com/my-yoono/sign_in.jsp';
  738.  
  739.     const MIME_STREAM_CID = "@mozilla.org/network/mime-input-stream;1";
  740.     const nsIMIMEInputStream = CI.nsIMIMEInputStream;
  741.     var mis = CL[MIME_STREAM_CID];
  742.     var headers = mis.createInstance(nsIMIMEInputStream);
  743.     headers.addHeader('X-Login', login);
  744.     headers.addHeader('X-Password', passwd);
  745.     if(!target) target = 'buzz';
  746.     if('webnote' == target) {
  747.       if(feedId) {
  748.         headers.addHeader('X-FeedId', feedId);
  749.       }
  750.       if(vsId) {
  751.         headers.addHeader('X-VsId', vsId);
  752.       }
  753.     }
  754.     headers.addHeader('X-Page', target);
  755.  
  756.     var obj   = Components.classes["@mozilla.org/io/string-input-stream;1"];
  757.     var iface = Components.interfaces.nsIStringInputStream;
  758.  
  759.     var win = WMED.getMostRecentWindow("navigator:browser");
  760.     var nav = win.getWebNavigation();
  761.     // todo : open in tab according to mouse button. Pb : sending headers !
  762.     nav.loadURI(url, 0, null, null, headers);
  763.   }
  764.  
  765.   this.hideYoonoStuff = function(win) {
  766.     this.closeSidebar(win);
  767.     this.hideYoonoButton(win);
  768.   }
  769.   this.closeSidebar = function(win) {
  770.     win.yoonoGlob.sidebar.hide();
  771.   }
  772.   this.hideYoonoButton = function(win) {
  773.     doc = win.document;
  774.     var toggle = doc.getElementById('yoono-toggle-sb');
  775.     if(toggle) toggle.setAttribute('hidden', 'true');
  776.   }
  777. }
  778.  
  779.  
  780. chromeUI.prototype = {
  781.   QueryInterface : function (iid) {
  782.     if(!iid.equals(CI.nsISupports) && !iid.equals(CI.nsIObserver))
  783.       throw Components.results.NS_ERROR_NO_INTERFACE;
  784.     return this;
  785.   },
  786.  
  787.   // l objet doit obligatoirement comporter une methode observe
  788.   observe: function(aSubject, aTopic, aData) {
  789.     YOONO_LOG.debug('YOONO_CMPT.observe : ' + aTopic);
  790.     switch(aTopic) {
  791.       case "app-startup":
  792.         var obsSvc = CL["@mozilla.org/observer-service;1"].getService(CI.nsIObserverService);
  793.         obsSvc.addObserver(this, "profile-after-change", false);
  794.         obsSvc.addObserver(this, "quit-application", false);
  795.         // LQ-20060925
  796.         obsSvc.addObserver(this, "profile-before-change", false);
  797.         obsSvc.addObserver(this, "profile-after-change", false);
  798.         obsSvc.addObserver(this, "xpcom-shutdown", false);
  799.       break;
  800.  
  801.       case "profile-after-change":
  802.  
  803.         break;
  804.       
  805.       case "quit-application":
  806.         this.getYServices().observer.notifyObservers("app.quit",null);
  807.         // Close the local database
  808.         YOONO_KEYVALUEDB.closeDB();
  809.         // LQ-20060925
  810.         YOONO_PREFS.storeLastModified(aTopic);
  811.         try {
  812.           if(this.uninstallWhenQuitting) {
  813.             this.getYServices().observer.notifyObservers("app.uninstall",null);
  814.             this.uninstall();
  815.           }
  816.         } catch(e) {
  817.           YOONO_LOG.exception(e);
  818.         }
  819.       break;
  820.  
  821.       // LQ-20060925
  822.       case "profile-before-change":
  823.         YOONO_PREFS.storeLastModified(aTopic);
  824.       break;
  825.       case "profile-after-change":
  826.         YOONO_PREFS.storeLastModified(aTopic);
  827.       break;
  828.       case "xpcom-shutdown":
  829.         YOONO_PREFS.storeLastModified(aTopic);
  830.       break;
  831.  
  832.       default:
  833.       throw Components.Exception("Unknown topic: " + aTopic);
  834.     }
  835.  
  836.   }
  837.  
  838. };
  839. //
  840.  
  841.  
  842. var YOONO_CMPT = new chromeUI();
  843.  
  844. } catch(e) {
  845. var console = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
  846. console.logStringMessage(e);
  847. }
  848.